﻿using SimpleInjector;
using Sustainalytics.Synchronization.Interface;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;

namespace Sustainalytics.Governance.Computing.ProfileTest
{
    class Program
    {

        private static Action<string, string> _logging;

        readonly static SyncContext _syncContext = new SyncContext()
        {
            GAConnectionStrings = new Dictionary<string, string>
            {
                { "ClientDW", "mongodb://localhost:27017/ClientDW_A" },
                { "GovernanceDW", "mongodb://localhost:27017/GovernanceDW_A" },
                { "GovernanceInputFile", "mongodb://54.154.132.43:27017/GovernanceInputFile" },
                //{ "GovernanceDW", "mongodb://54.72.160.155:27017/GovernanceDW_A" },
                //{ "GovernanceInputFile", "mongodb://54.72.160.155:27017/GovernanceInputFile" }
            },
            Report = new ConcurrentBag<string>()
        };

        protected static Container SimpleInjectorContainer;

        static void Main(string[] args)
        {
            var issues = "CG.6";

            SimpleInjectorContainer = SimpleInjectorConfig.Register(new SyncContextLogger(_syncContext), _syncContext.GAConnectionStrings);

            var computationEngine = SimpleInjectorContainer.GetInstance<GovernanceComputationEngine>();


            computationEngine.Compute();

            var logging = computationEngine.Log;

            //var dataSet = dataProvider.GetDataContainer();

            //var setId = AddGovernanceSet(dataSet.SetId, dataSet.Companies.ToDomainModel(), dataSet.IndicatorTemplates, dataSet.CompaniesRawScores, dataSet.WeightMatrix, dataSet.Modifiers);
            //var computation = new GovernanceComputation();
            //var computation = GovernanceComputationEngine.Instance(dataProvider);
        }

        //private IndicatorsConfiguration Configuration { get; set; }
        //private GovernanceSet Set { get; set; }


        //public Guid AddGovernanceSet(Guid? setId, ICollection<GovernanceCompanyEntity> companies, ICollection<IndicatorTemplate> indicatorTemplates,
        //    IEnumerable<CompanyIndicators<IndicatorScore>> companiesRawScores, RegionMatrix matrix, ICollection<CombinationModifier> modifiers)
        //{

        //    if (!setId.HasValue || setId.Equals(Guid.Empty))
        //        setId = Guid.NewGuid();

        //    var factory = new ESGIndicatorConfigurationFactory();
        //    Configuration = factory.GetBuilder().GetConfiguration(indicatorTemplates);

        //    Set = new GovernanceSet(null, Configuration, companies);


        //    Set.AddIndicatorsRawScore(companiesRawScores);
        //    Set.AddMatrix(matrix.MatrixId, matrix.Matrix);
        //    Set.AddCombinationModifiers(matrix.MatrixId, modifiers);
        //    Set.AddIssueWeights(matrix.MatrixId, matrix.IssuesWeights);

        //    return setId.GetValueOrDefault();

        //}


        const int MAX_DISJUNCT_CLUSTERS_COUNT = 8;

        private void ComputeGovernanceRatings(
                float[] weights,
                float[] scores,
                float[] modifiers,
                float[] keyIssuesWeight,
                float[] ratings,
                float[] key_issues_weightedScores,
                float[] key_issues_weights,
                int companiesNo,
                int indicatorsNo,
                int issuesCount)
        {

            int line = 0; //(blockDim.x * blockIdx.x + threadIdx.x);
            if (line >= companiesNo)
                return;

            int[] clusterStarts = new int[MAX_DISJUNCT_CLUSTERS_COUNT] { 0, 0, 0, 0, 0, 0, 0, 0 };
            int[] clusterGroups = new int[MAX_DISJUNCT_CLUSTERS_COUNT] { 0, 0, 0, 0, 0, 0, 0, 0 };

            float[] clusterGroupsResults = new float[MAX_DISJUNCT_CLUSTERS_COUNT] { 0, 0, 0, 0, 0, 0, 0, 0 };


            float clusterGroupScore = 0;
            float clusterGroupWeight = 0;
            byte currentClusterGroup = 0;
            int lastClusterStartIndex = 0;
            int lastClusterEndIndex = clusterStarts[currentClusterGroup];
            float modifierValue = 0;


            int index = line;


            for (int i = 0; i < indicatorsNo; i++)
            {
                if (i == clusterStarts[currentClusterGroup])
                {
                    clusterGroupsResults[clusterGroups[currentClusterGroup]] = modifierValue + (clusterGroupScore + clusterGroupsResults[clusterGroups[currentClusterGroup]]) / clusterGroupWeight;

                    lastClusterStartIndex = i;
                    ++currentClusterGroup;

                    lastClusterEndIndex = i > clusterGroups[currentClusterGroup]
                        ? indicatorsNo
                        : clusterGroups[currentClusterGroup];

                    clusterGroupScore = 0;
                    clusterGroupWeight = 0;
                    modifierValue = 0;
                }

                float weightCurrent = weights[index];
                float scoreCurrent = NormalizeRawScore(scores[index]);
                float indicatorRating = weightCurrent * scoreCurrent;

                //𝑀𝑜𝑑𝑖𝑓𝑖𝑒𝑟 = ∑𝑖 = 1, 𝑛∑𝑗 = 𝑖 + 1,𝑛−1    𝑤𝑖,𝑗 ×(𝑣𝑖 + 𝑣𝑗)× | 𝑣𝑖𝑣𝑗 |
                for (int j = lastClusterStartIndex; j < lastClusterEndIndex; j++)
                {
                    float weight = modifiers[i * indicatorsNo + j];
                    float adjacentScore = NormalizeRawScore(scores[line + j * companiesNo]);

                    //modifierValue = modifierValue + (scoreCurrent + adjacentScore) * fabsf(scoreCurrent * adjacentScore) * weight;
                    modifierValue = modifierValue + (scoreCurrent + adjacentScore) * Math.Abs(scoreCurrent * adjacentScore) * weight;
                }

                clusterGroupScore += indicatorRating;
                clusterGroupWeight += weightCurrent;
                index = index + companiesNo;
            }

            clusterGroupsResults[clusterGroups[currentClusterGroup]] = modifierValue + (clusterGroupScore + clusterGroupsResults[clusterGroups[currentClusterGroup]]) / clusterGroupWeight;


            float issueWeight = 0;
            float finalScore = 0;
            float issuescore;

            //#pragma unroll
            for (int i = 0; i < issuesCount; i++)
            {
                index = (1 + i) * companiesNo + line;
                ratings[index] = DenormalizeRawScore(clusterGroupsResults[i]);

                issuescore = keyIssuesWeight[i * companiesNo + line];
                issueWeight += issuescore;

                if (key_issues_weightedScores.Any())
                {
                    key_issues_weightedScores[index] = ratings[index] * issuescore;
                }

                if (key_issues_weights.Any())
                {
                    key_issues_weights[index] = issuescore;
                }

                finalScore += ratings[index] * issuescore;
            }


            ratings[line] = finalScore / issueWeight;

            if (key_issues_weightedScores.Any())
            {
                key_issues_weightedScores[line] = ratings[line];
            }

            if (key_issues_weights.Any())
            {
                key_issues_weights[line] = issueWeight;
            }
        }

        private float NormalizeRawScore(float score)
        {
            return (score - 50) / 50;
        }

        private float DenormalizeRawScore(float score)
        {
            float denormalized = (score * 50 + 50);
            float cappedScore;
            cappedScore = (denormalized < 0) ? 0 : denormalized;
            cappedScore = (cappedScore > 100) ? 100 : cappedScore;

            return cappedScore;
        }
    }
}
